home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Codigo / hh / rsource.exe / Hexen Source / OLDD_NET.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-01-12  |  16.2 KB  |  791 lines

  1. // I_pcnet.m
  2.  
  3. #include "DoomDef.h"
  4.  
  5. #define    NCMD_EXIT        0x80000000
  6. #define    NCMD_RETRANSMIT    0x40000000
  7. #define    NCMD_SETUP        0x20000000
  8. #define    NCMD_CHECKSUM    0x0fffffff
  9.  
  10. /*
  11. if more space needs to be crunched out of the protocol...
  12.  
  13. 1    drone
  14. 2    player
  15. 8    tic
  16. 5    numtics
  17.  
  18. #define    NCMD_EXIT        0x80000000
  19. #define    NCMD_RETRANSMIT    0x40000000            // a retransmit will have 0 tics
  20. #define    NCMD_DRONE        0x20000000
  21. #define    NCMD_PLAYER        0x18000000
  22. #define    NCMD_PLAYERSHIFT    27
  23. #define    NCMD_TIC        0x00ff0000
  24. #define    NCMD_TICSHIFT    16
  25. #define    NCMD_NUMTICS    0x0000ff00
  26. #define    NCMD_NUMTICSSHIFT    8
  27. #define    NCMD_CHECKSUM    0x000000ff
  28.  
  29. */
  30.  
  31.  
  32.  
  33.  
  34.  
  35. doomcom_t        *doomcom;    
  36. doomdata_t        *netbuffer;        // points inside doomcom
  37.  
  38.  
  39. /*
  40. ==============================================================================
  41.  
  42.                             NETWORKING
  43.  
  44. gametic is the tic about to (or currently being) run
  45. maketic is the tick that hasn't had control made for it yet
  46. nettics[] has the maketics for all players 
  47.  
  48. a gametic cannot be run until nettics[] > gametic for all players
  49.  
  50. ==============================================================================
  51. */
  52.  
  53. #define    RESENDCOUNT    10
  54. #define    PL_DRONE    0x80                // bit flag in doomdata->player
  55.  
  56. ticcmd_t        localcmds[BACKUPTICS];
  57.  
  58. ticcmd_t        netcmds[MAXPLAYERS][BACKUPTICS];
  59. int             nettics[MAXNETNODES];
  60. boolean            nodeingame[MAXNETNODES];    // set false as nodes leave game
  61. boolean            remoteresend[MAXNETNODES];    // set when local needs tics
  62. int                resendto[MAXNETNODES];            // set when remote needs tics
  63. int                resendcount[MAXNETNODES];
  64.  
  65. int                nodeforplayer[MAXPLAYERS];
  66.  
  67. int             gametime;
  68. int             maketic;
  69. int                lastnettic, skiptics;
  70. int                ticdup;        
  71.  
  72. void D_ProcessEvents (void);
  73. void G_BuildTiccmd (ticcmd_t *cmd);
  74. void D_DoAdvanceDemo (void);
  75.  
  76. boolean            reboundpacket;
  77. doomdata_t        reboundstore;
  78.  
  79.  
  80. int    NetbufferSize (void)
  81. {
  82.     return (int)&(((doomdata_t *)0)->cmds[netbuffer->numtics]); 
  83. }
  84.  
  85. unsigned NetbufferChecksum (void)
  86. {
  87.     unsigned        c;
  88.     int        i,l;
  89.  
  90.     c = 0x1234567;
  91.  
  92. #ifdef NeXT
  93.     return 0;            // byte order problems
  94. #endif
  95.  
  96.     l = (NetbufferSize () - (int)&(((doomdata_t *)0)->retransmitfrom))/4;
  97.     for (i=0 ; i<l ; i++)
  98.         c += ((unsigned *)&netbuffer->retransmitfrom)[i] * (i+1);
  99.  
  100.     return c & NCMD_CHECKSUM;
  101. }
  102.  
  103. int ExpandTics (int low)
  104. {
  105.     int    delta;
  106.     
  107.     delta = low - (maketic&0xff);
  108.     
  109.     if (delta >= -64 && delta <= 64)
  110.         return (maketic&~0xff) + low;
  111.     if (delta > 64)
  112.         return (maketic&~0xff) - 256 + low;
  113.     if (delta < -64)
  114.         return (maketic&~0xff) + 256 + low;
  115.         
  116.     I_Error ("ExpandTics: strange value %i at maketic %i",low,maketic);
  117.     return 0;
  118. }
  119.  
  120.  
  121. //============================================================================
  122.  
  123.  
  124. /*
  125. ==============
  126. =
  127. = HSendPacket
  128. =
  129. ==============
  130. */
  131.  
  132. void HSendPacket (int node, int flags)
  133. {
  134.     netbuffer->checksum = NetbufferChecksum () | flags;
  135.  
  136.     if (!node)
  137.     {
  138.         reboundstore = *netbuffer;
  139.         reboundpacket = true;
  140.         return;
  141.     }
  142.  
  143.     if (!netgame)
  144.         I_Error ("Tried to transmit to another node");
  145.         
  146.     doomcom->command = CMD_SEND;
  147.     doomcom->remotenode = node;
  148.     doomcom->datalength = NetbufferSize ();
  149.     
  150. if (debugfile)
  151. {
  152.     int        i;
  153.     int        realretrans;
  154.     if (netbuffer->checksum & NCMD_RETRANSMIT)
  155.         realretrans = ExpandTics (netbuffer->retransmitfrom);
  156.     else
  157.         realretrans = -1;
  158.     fprintf (debugfile,"send (%i + %i, R %i) [%i] "
  159.     ,ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
  160.     for (i=0 ; i<doomcom->datalength ; i++)
  161.         fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
  162.     fprintf (debugfile,"\n");
  163. }
  164.  
  165.     I_NetCmd ();
  166. }
  167.  
  168. /*
  169. ==============
  170. =
  171. = HGetPacket
  172. =
  173. = Returns false if no packet is waiting
  174. =
  175. ==============
  176. */
  177.  
  178. boolean HGetPacket (void)
  179. {    
  180.     if (reboundpacket)
  181.     {
  182.         *netbuffer = reboundstore;
  183.         doomcom->remotenode = 0;
  184.         reboundpacket = false;
  185.         return true;
  186.     }
  187.  
  188.     if (!netgame)
  189.         return false;
  190.         
  191.     doomcom->command = CMD_GET;
  192.     I_NetCmd ();
  193.     if (doomcom->remotenode == -1)
  194.         return false;
  195.  
  196.     if (doomcom->datalength != NetbufferSize ())
  197.     {
  198.         if (debugfile)
  199.             fprintf (debugfile,"bad packet length %i\n",doomcom->datalength);
  200.         return false;
  201.     }
  202.     
  203.     if (NetbufferChecksum () != (netbuffer->checksum&NCMD_CHECKSUM) )
  204.     {
  205.         if (debugfile)
  206.             fprintf (debugfile,"bad packet checksum\n");
  207.         return false;
  208.     }
  209.  
  210. if (debugfile)
  211. {
  212.     int        realretrans;
  213.             int    i;
  214.             
  215.     if (netbuffer->checksum & NCMD_SETUP)
  216.         fprintf (debugfile,"setup packet\n");
  217.     else
  218.     {
  219.         if (netbuffer->checksum & NCMD_RETRANSMIT)
  220.             realretrans = ExpandTics (netbuffer->retransmitfrom);
  221.         else
  222.             realretrans = -1;
  223.         fprintf (debugfile,"get %i = (%i + %i, R %i)[%i] ",doomcom->remotenode,
  224.         ExpandTics(netbuffer->starttic),netbuffer->numtics, realretrans, doomcom->datalength);
  225.         for (i=0 ; i<doomcom->datalength ; i++)
  226.             fprintf (debugfile,"%i ",((byte *)netbuffer)[i]);
  227.         fprintf (debugfile,"\n");
  228.     }
  229. }
  230.     return true;    
  231. }
  232.  
  233.  
  234. /*
  235. ===================
  236. =
  237. = GetPackets
  238. =
  239. ===================
  240. */
  241.  
  242. char    exitmsg[80];
  243.  
  244. void GetPackets (void)
  245. {
  246.     int        netconsole;
  247.     int        netnode;
  248.     int        netdrone;
  249.     int        j;
  250.     ticcmd_t    *src, *dest;
  251.     int        dupedstart, dupedend;
  252.     int        skiptics;
  253.     int        realstart;
  254.                  
  255.     while (HGetPacket ())
  256.     {
  257.         if (netbuffer->checksum & NCMD_SETUP)
  258.             continue;        // extra setup packet
  259.             
  260.         netdrone = netbuffer->player & PL_DRONE;
  261.         netconsole = netbuffer->player & ~PL_DRONE;
  262.         netnode = doomcom->remotenode;
  263.         //
  264.         // to save bytes, only the low byte of tic numbers are sent
  265.         // Figure out what the rest of the bytes are
  266.         //
  267.         realstart = ExpandTics (netbuffer->starttic);        
  268.         dupedstart = realstart*doomcom->ticdup;
  269.         dupedend = (realstart+netbuffer->numtics)*doomcom->ticdup;
  270.         
  271.         //
  272.         // check for exiting the game
  273.         //
  274.         if (netbuffer->checksum & NCMD_EXIT)
  275.         {
  276.             if (!nodeingame[netnode])
  277.                 continue;
  278.             nodeingame[netnode] = false;
  279.             if (!netdrone)
  280.             {
  281.                 playeringame[netconsole] = false;
  282.                 strcpy (exitmsg, "Player 1 left the game");
  283.                 exitmsg[7] += netconsole;
  284.                 players[consoleplayer].message = exitmsg;
  285.             }
  286.             continue;
  287.         }
  288.  
  289.         //
  290.         // drone packets are just notifications
  291.         //
  292.         if (netdrone)
  293.         {
  294.             nettics[netnode] = dupedend;
  295.             continue;
  296.         }
  297.  
  298.         nodeforplayer[netconsole] = netnode;
  299.         
  300.         //
  301.         // check for retransmit request
  302.         //
  303.         if ( resendcount[netnode] <= 0 
  304.         && (netbuffer->checksum & NCMD_RETRANSMIT) )
  305.         {
  306.             resendto[netnode] = ExpandTics(netbuffer->retransmitfrom);
  307. if (debugfile)
  308. fprintf (debugfile,"retransmit from %i\n", resendto[netnode]);
  309.             resendcount[netnode] = RESENDCOUNT;
  310.         }
  311.         else
  312.             resendcount[netnode]--;
  313.  
  314.         //
  315.         // check for out of order / duplicated packet
  316.         //        
  317.         if (dupedend == nettics[netnode])
  318.             continue;
  319.             
  320.         if (dupedend < nettics[netnode])
  321.         {
  322. if (debugfile)
  323. fprintf (debugfile,"out of order packet (%i + %i)\n" ,realstart,netbuffer->numtics);
  324.             continue;
  325.         }
  326.  
  327.         //
  328.         // check for a missed packet
  329.         //
  330.         if (dupedstart > nettics[netnode])
  331.         {
  332.         // stop processing until the other system resends the missed tics
  333. if (debugfile)
  334. fprintf (debugfile,"missed tics from %i (%i - %i)\n", netnode, dupedstart, nettics[netnode]);
  335.             remoteresend[netnode] = true;
  336.             continue;
  337.         }
  338.     
  339. //
  340. // update command store from the packet
  341. //
  342.         remoteresend[netnode] = false;
  343.         
  344.         skiptics = nettics[netnode]/doomcom->ticdup - realstart;        
  345.         src = &netbuffer->cmds[skiptics];
  346.  
  347.         while (nettics[netnode] < dupedend)
  348.         {
  349.             for (j=0 ; j<doomcom->ticdup ; j++)
  350.             {
  351.                 dest = &netcmds[netconsole][nettics[netnode]%BACKUPTICS];
  352.                 nettics[netnode]++;
  353.                 *dest = *src;
  354.                 src->chatchar = 0;
  355.                 if (src->buttons & BT_SPECIAL)
  356.                     src->buttons = 0;
  357.             }
  358.             src++;
  359.         }
  360.     }
  361. }
  362.  
  363. /*
  364. =============
  365. =
  366. = NetUpdate
  367. =
  368. = Builds ticcmds for console player
  369. = sends out a packet
  370. =============
  371. */
  372.  
  373. void NetUpdate (void)
  374. {
  375.     int             nowtime;
  376.     int             newtics;
  377.     int                i,j;
  378.     int                gameticdiv;
  379.     int                realstart;
  380.         
  381.     if (singletics)
  382.         return;         // singletic update is syncronous
  383.         
  384. //
  385. // check time
  386. //      
  387.     nowtime = I_GetTime ()/doomcom->ticdup;
  388.     newtics = nowtime - gametime;
  389.     gametime = nowtime;
  390.     if (newtics <= 0)                       // nothing new to update
  391.         goto listen; 
  392.  
  393.     if (skiptics <= newtics)
  394.     {
  395.         newtics -= skiptics;
  396.         skiptics = 0;
  397.     }
  398.     else
  399.     {
  400.         skiptics -= newtics;
  401.         newtics = 0;
  402.     }
  403.     
  404.         
  405.     netbuffer->player = consoleplayer;
  406.     if (doomcom->drone)
  407.         netbuffer->player |= PL_DRONE;
  408.     
  409. //
  410. // drone packets
  411. //
  412.     if (doomcom->drone)
  413.     {
  414.         I_StartTic ();
  415.         D_ProcessEvents ();
  416.         goto sendit;
  417.     }
  418.     
  419. //
  420. // build new ticcmds for console player
  421. //
  422.     gameticdiv = (gametic+doomcom->ticdup-1)/doomcom->ticdup;
  423.     for (i=0 ; i<newtics ; i++)
  424.     {
  425.         I_StartTic ();
  426.         D_ProcessEvents ();
  427.         if (maketic - gameticdiv >= BACKUPTICS/2 /* /doomcom->ticdup */- 1)
  428.         {
  429.             newtics = i;
  430.             break;          // can't hold any more
  431.         }
  432. //printf ("mk:%i ",maketic);
  433.         G_BuildTiccmd (&localcmds[maketic%BACKUPTICS]);
  434.         maketic++;
  435.     }
  436.  
  437. //
  438. // send the packet to the other nodes
  439. //
  440. sendit:
  441.     for (i=0 ; i<doomcom->numnodes ; i++)
  442.         if (nodeingame[i])
  443.         {
  444.             if (doomcom->drone)
  445.             {
  446.                 netbuffer->starttic = realstart = maketic + BACKUPTICS/2;
  447.                 netbuffer->numtics = 0;
  448.             }
  449.             else
  450.             {
  451.                 netbuffer->starttic = realstart = resendto[i];
  452.                 netbuffer->numtics = maketic - realstart;
  453.                 resendto[i] = maketic - doomcom->extratics;
  454.             }
  455.     
  456.             if (netbuffer->numtics > BACKUPTICS)
  457.                 I_Error ("NetUpdate: netbuffer->numtics > BACKUPTICS");
  458.  
  459.             for (j=0 ; j< netbuffer->numtics ; j++)
  460.                 netbuffer->cmds[j] = 
  461.                     localcmds[(realstart+j)%BACKUPTICS];
  462.                     
  463.             if (remoteresend[i])
  464.             {
  465.                 netbuffer->retransmitfrom = nettics[i]/doomcom->ticdup;
  466.                 HSendPacket (i, NCMD_RETRANSMIT);
  467.             }
  468.             else
  469.             {
  470.                 netbuffer->retransmitfrom = 0;
  471.                 HSendPacket (i, 0);
  472.             }
  473.         }
  474.  
  475. //
  476. // listen for other packets
  477. //        
  478. listen:
  479.  
  480.     GetPackets ();
  481. }
  482.  
  483.  
  484. /*
  485. =====================
  486. =
  487. = CheckAbort
  488. =
  489. =====================
  490. */
  491.  
  492. void CheckAbort (void)
  493. {
  494.     event_t *ev;
  495.     
  496.     I_WaitVBL(2);
  497.     
  498.     I_StartTic ();
  499.     for ( ; eventtail != eventhead 
  500.     ; eventtail = (++eventtail)&(MAXEVENTS-1) )
  501.     {
  502.         ev = &events[eventtail];
  503.         if (ev->type == ev_keydown && ev->data1 == KEY_ESCAPE)
  504.             I_Error ("Network game synchronization aborted.");
  505.     }
  506. }
  507.  
  508. /*
  509. =====================
  510. =
  511. = D_ArbitrateNetStart
  512. =
  513. =====================
  514. */
  515.  
  516. void D_ArbitrateNetStart (void)
  517. {
  518.     int        i;
  519.     boolean    gotinfo[MAXNETNODES];
  520.     
  521.     autostart = true;
  522.     memset (gotinfo,0,sizeof(gotinfo));
  523.     
  524.     if (doomcom->consoleplayer)
  525.     {    // listen for setup info from key player
  526.         printf ("listening for network start info...\n");
  527.         while (1)
  528.         {
  529.             CheckAbort ();
  530.             if (!HGetPacket ())
  531.                 continue;
  532.             if (netbuffer->checksum & NCMD_SETUP)
  533.             {
  534.                 if (netbuffer->player != VERSION)
  535.                     I_Error ("Different DOOM versions cannot play a net game!");
  536.                 startskill = netbuffer->retransmitfrom & 15;
  537.                 deathmatch = (netbuffer->retransmitfrom & 0x80) > 0;
  538.                 nomonsters = (netbuffer->retransmitfrom & 0x40) > 0;
  539.                 respawnparm = (netbuffer->retransmitfrom & 0x20) > 0;
  540.                 startmap = netbuffer->starttic & 15;
  541.                 startepisode = netbuffer->starttic >> 4;
  542.                 return;
  543.             }
  544.         }
  545.     }
  546.     else
  547.     {    // key player, send the setup info
  548.         printf ("sending network start info...\n");
  549.         do
  550.         {
  551.             CheckAbort ();
  552.             for (i=0 ; i<doomcom->numnodes ; i++)
  553.             {
  554.                 netbuffer->retransmitfrom = startskill;
  555.                 if (deathmatch)
  556.                     netbuffer->retransmitfrom |= 0x80;
  557.                 if (nomonsters)
  558.                     netbuffer->retransmitfrom |= 0x40;
  559.                 if (respawnparm)
  560.                     netbuffer->retransmitfrom |= 0x20;
  561.                 netbuffer->starttic = startepisode * 16 + startmap;
  562.                 netbuffer->player = VERSION;
  563.                 netbuffer->numtics = 0;
  564.                 HSendPacket (i, NCMD_SETUP);
  565.             }
  566.     
  567.             while (HGetPacket ())
  568.             {
  569.                 gotinfo[netbuffer->player&0x7f] = true;
  570.             }
  571.  
  572.             for (i=1 ; i<doomcom->numnodes ; i++)
  573.                 if (!gotinfo[i])
  574.                     break;
  575.         } while (i < doomcom->numnodes);
  576.     }
  577. }
  578.  
  579. /*
  580. ===================
  581. =
  582. = D_CheckNetGame
  583. =
  584. = Works out player numbers among the net participants
  585. ===================
  586. */
  587.  
  588. extern    int            viewangleoffset;
  589.  
  590. void D_CheckNetGame (void)
  591. {
  592.     int             i;
  593.     
  594.     for (i=0 ; i<MAXNETNODES ; i++)
  595.     {
  596.         nodeingame[i] = false;
  597.            nettics[i] = 0;
  598.         remoteresend[i] = false;    // set when local needs tics
  599.         resendto[i] = 0;            // which tic to start sending
  600.     }
  601.     
  602. // I_InitNetwork sets doomcom and netgame
  603.     I_InitNetwork ();
  604.     if (doomcom->id != DOOMCOM_ID)
  605.         I_Error ("Doomcom buffer invalid!");
  606.     netbuffer = &doomcom->data;
  607.     consoleplayer = displayplayer = doomcom->consoleplayer;
  608.     if (netgame)
  609.         D_ArbitrateNetStart ();
  610. printf ("startskill %i  deathmatch: %i  startmap: %i  startepisode: %i\n", startskill, deathmatch, startmap, startepisode);
  611.     
  612. // read values out of doomcom
  613.     ticdup = doomcom->ticdup;
  614.                     
  615.     for (i=0 ; i<doomcom->numplayers ; i++)
  616.         playeringame[i] = true;
  617.     for (i=0 ; i<doomcom->numnodes ; i++)
  618.         nodeingame[i] = true;
  619.     
  620. printf ("player %i of %i (%i nodes)\n", consoleplayer+1, doomcom->numplayers, doomcom->numnodes);
  621.  
  622. }
  623.  
  624. /*
  625. ==================
  626. =
  627. = D_QuitNetGame
  628. =
  629. = Called before quitting to leave a net game without hanging the
  630. = other players
  631. =
  632. ==================
  633. */
  634.  
  635. void D_QuitNetGame (void)
  636. {
  637.     int             i, j;
  638.     
  639.     if (debugfile)
  640.         fclose (debugfile);
  641.         
  642.     if (!netgame || !usergame || consoleplayer == -1)
  643.         return;
  644.     
  645. // send a bunch of packets for security
  646.     netbuffer->player = consoleplayer;
  647.     if (doomcom->drone)
  648.         netbuffer->player |= PL_DRONE;
  649.     netbuffer->numtics = 0;
  650.     for (i=0 ; i<4 ; i++)
  651.     {
  652.         for (j=1 ; j<doomcom->numnodes ; j++)
  653.             if (nodeingame[j])
  654.                 HSendPacket (j, NCMD_EXIT);
  655.         I_WaitVBL (1);
  656.     }
  657. }
  658.  
  659.  
  660.  
  661. /*
  662. ===============
  663. =
  664. = TryRunTics
  665. =
  666. ===============
  667. */
  668.  
  669. int    frametics[4], frameon;
  670. int    frameskip[4];
  671. int        oldnettics;
  672. extern    boolean    advancedemo;
  673.  
  674. void TryRunTics (void)
  675. {
  676.     int             i;
  677.     int             lowtic, nextlowest;
  678.     int             entertic;
  679.     int    static        oldentertics;
  680.     int                realtics, availabletics;
  681.     int                counts;
  682.     int                numplaying;
  683.  
  684. //
  685. // get real tics
  686. //            
  687.     entertic = I_GetTime ();
  688.     realtics = entertic - oldentertics;
  689.     oldentertics = entertic;
  690.  
  691. //
  692. // get available tics
  693. //
  694.     NetUpdate ();
  695.     
  696.     lowtic = nextlowest = MAXINT;
  697.     numplaying = 0;
  698.     for (i=0 ; i<doomcom->numnodes ; i++)
  699.         if (nodeingame[i])
  700.         {
  701.             numplaying++;
  702.             if (nettics[i] < lowtic)
  703.             {
  704.                 nextlowest = lowtic;
  705.                 lowtic = nettics[i];
  706.             }
  707.             else if (nettics[i] < nextlowest)
  708.                 nextlowest = nettics[i]; 
  709.         }
  710.     availabletics = lowtic - gametic;
  711.     
  712.  
  713. //
  714. // decide how many tics to run
  715. //
  716.     if (realtics < availabletics-1)
  717.         counts = realtics+1;
  718.     else if (realtics < availabletics)
  719.         counts = realtics;
  720.     else
  721.         counts = availabletics;
  722.     if (counts < 1)
  723.         counts = 1;
  724.         
  725.     frameon++;
  726.     
  727. if (debugfile)
  728.     fprintf (debugfile,"=======real: %i  avail: %i  game: %i\n",realtics, availabletics,counts);
  729.  
  730. //=============================================================================
  731. //
  732. //    ideally nettics[0] should be 1 - 3 tics above lowtic
  733. //    if we are consistantly slower, speed up time
  734. //    drones should never hold up the other players
  735. //
  736.     for (i=0 ; i<MAXPLAYERS ; i++)
  737.         if (playeringame[i])
  738.             break;
  739.     if (consoleplayer == i)
  740.     {    // the key player does not adapt
  741.     }
  742.     else
  743.     {
  744.         if (nettics[0] <= nettics[nodeforplayer[i]])
  745.         {
  746.             gametime--;
  747. //            printf ("-");
  748.         }
  749.         frameskip[frameon&3] = (oldnettics > nettics[nodeforplayer[i]]);
  750.         oldnettics = nettics[0];
  751.         if (frameskip[0] && frameskip[1] && frameskip[2] && frameskip[3])
  752.         {
  753.             skiptics = 1;
  754. //            printf ("+");
  755.         }
  756.     }
  757. //=============================================================================
  758.  
  759. //
  760. // wait for new tics if needed
  761. //
  762.     while (lowtic < gametic + counts)    
  763.     {
  764.  
  765.         NetUpdate ();   
  766.         lowtic = MAXINT;
  767.         
  768.         for (i=0 ; i<doomcom->numnodes ; i++)
  769.             if (nodeingame[i] && nettics[i] < lowtic)
  770.                 lowtic = nettics[i];
  771.  
  772.         if (lowtic < gametic)
  773.             I_Error ("TryRunTics: lowtic < gametic");
  774.             
  775.         // don't stay in here forever -- give the menu a chance to work
  776.         if (I_GetTime () - entertic >= 20)
  777.             return;
  778.     }
  779.             
  780.  
  781. //
  782. // run the tics
  783. //    
  784.     while (counts--)
  785.     {
  786.         G_Ticker ();
  787.         NetUpdate ();                    // check for new console commands
  788.         gametic++;
  789.     }
  790. }
  791.